#pragma once
// -------------------------------------------------
// ZED Kit
// -------------------------------------------------
//   File Name: log.hpp
//      Author: Ziming Li
//     Created: 2021-02-17
// -------------------------------------------------
// Copyright (C) 2021 MingYang Software Technology.
// -------------------------------------------------

#ifndef ZED_LOG_HPP
#define ZED_LOG_HPP

#include "./platform_sdk.h"
#include <version>
#ifdef __cpp_lib_format
#   include <format>
#else
#   include "./string/format.hpp"
#endif
#ifdef _Z_OS_WINDOWS
#   include "./locale.hpp"
#endif

namespace zed {

template <typename... Args>
void log(const char* fmt, Args&&... args);

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementations

namespace detail {
inline void log_impl(const std::string& s)
{
#if defined(_Z_OS_WINDOWS)
    std::wstring ws = zed::multi_byte_to_wide_string(s);
    ws.append(L"\r\n");
    ::OutputDebugStringW(ws.c_str());
#elif defined(_Z_OS_POSIX)
    fprintf(stderr, "%s\n", s.c_str());
#endif
}
} // namespace detail

#ifdef __cpp_lib_format
template <typename... Args>
void log(const char* fmt, Args&&... args)
{
    std::string s = std::vformat(fmt, std::make_format_args(args...));
    detail::log_impl(s);
}
#else
struct log_serializer {
    template <typename T>
    static void push(std::vector<std::string>& dst, const T& arg);

    static void push(std::vector<std::string>& dst, bool b) {
        push(dst, b ? "true" : "false");
    }
    static void push(std::vector<std::string>& dst, short s) {
        push(dst, std::to_string(s));
    }
    static void push(std::vector<std::string>& dst, unsigned short us) {
        push(dst, std::to_string(us));
    }
    static void push(std::vector<std::string>& dst, int n) {
        push(dst, std::to_string(n));
    }
    static void push(std::vector<std::string>& dst, unsigned int u) {
        push(dst, std::to_string(u));
    }
    static void push(std::vector<std::string>& dst, long l) {
        push(dst, std::to_string(l));
    }
    static void push(std::vector<std::string>& dst, unsigned long ul) {
        push(dst, std::to_string(ul));
    }
    static void push(std::vector<std::string>& dst, const std::string& s) {
        dst.emplace_back(s);
    }
    static void push(std::vector<std::string>& dst, const std::string_view& sv) {
        dst.emplace_back(sv);
    }
    static void push(std::vector<std::string>& dst, const char* psz) {
        dst.emplace_back(psz);
    }
};

template <typename... Args>
void log(const char* fmt, Args&&... args)
{
    args_collector<log_serializer> ac;
    std::string s = detail::sequence_format(ac, fmt, args...);
    detail::log_impl(s);
}

inline void log(const char* s)
{
#if defined(_Z_OS_WINDOWS)
    std::wstring ws = multi_byte_to_wide_string(s);
    ws.append(L"\r\n");
    ::OutputDebugStringW(ws.c_str());
#elif defined(_Z_OS_POSIX)
    fprintf(stderr, "%s\n", s);
#endif
}
#endif // __cpp_lib_format

} // namespace zed

#ifdef NDEBUG
#   define ZLOG(...)    ((void)0)
#else
#   define ZLOG         ::zed::log
#endif

#endif // ZED_LOG_HPP
